package org.bigdata.framework.core.websocket;

import java.util.Date;
import java.util.Set;

import javax.websocket.server.ServerEndpoint;

import org.bytedeco.javacpp.opencv_core.RefOrVoid.type;
import org.springframework.stereotype.Component;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import net.sf.json.JSONObject;

/**
 * ClassName:MyWebSocketServerHandler Function: TODO ADD FUNCTION.
 *
 * @author hxy
 */
@Component
@ServerEndpoint("/websocket/{id}")
public class MyWebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
	private WebSocketServerHandshaker handshaker;

	/**
	 * channel 通道 action 活跃的
	 * 当客户端主动链接服务端的链接后，这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		// 添加
		//Global.group.add(ctx.channel());
		System.out.println("客户端与服务端连接开启：" + ctx.channel().remoteAddress().toString());
	}
	/**
	 * channel 通道 Inactive 不活跃的
	 * 当客户端主动断开服务端的链接后，这个通道就是不活跃的。也就是说客户端与服务端关闭了通信通道并且不可以传输数据
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		// 移除
		Object id = ctx.attr(AttributeKey.valueOf("id")).get();
		if(id!=null){
			Global.delChannelGroup(id.toString(), ctx.channel());
			JSONObject o = new JSONObject();
			o.put("onlineNumber", Global.getOnlineCount(id.toString()));
			TextWebSocketFrame tws = new TextWebSocketFrame(o.toString());
			o=null;
			sendMessage(id.toString(),tws);
			tws=null;
		}
		System.out.println("客户端与服务端连接关闭：" + ctx.channel().remoteAddress().toString());
		id=null;
		
	}

	/**
	 * 接收客户端发送的消息 channel 通道 Read 读
	 * 简而言之就是从通道中读取数据，也就是服务端接收客户端发来的数据。但是这个数据在不进行解码时它是ByteBuf类型的
	 */
	@Override
	protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
		// 传统的HTTP接入
		if (msg instanceof FullHttpRequest) {
			handleHttpRequest(ctx, ((FullHttpRequest) msg));//建立连接时才执行
			// WebSocket接入
		} else if (msg instanceof WebSocketFrame) {
			if ("anzhuo".equals(ctx.attr(AttributeKey.valueOf("type")).get())) {
			  	handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
			} else {
				handlerWebSocketFrame2(ctx, (WebSocketFrame) msg);
			}
			//handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
		}
	}

	/**
	 * channel 通道 Read 读取 Complete 完成 在通道读取完成后会在这个方法里通知，对应可以做刷新操作 ctx.flush()
	 */
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}

	private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
		// 判断是否关闭链路的指令
		if (frame instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
			return;
		}
		// 判断是否ping消息
		if (frame instanceof PingWebSocketFrame) {
			ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
			return;
		}
		// 本例程仅支持文本消息，不支持二进制消息
		if (!(frame instanceof TextWebSocketFrame)) {
			System.out.println("本例程仅支持文本消息，不支持二进制消息");
			throw new UnsupportedOperationException(
					String.format("%s frame types not supported", frame.getClass().getName()));
		}
		// 返回应答消息
		String request = ((TextWebSocketFrame) frame).text();
		//System.out.println("服务端收到：" + request);
		//TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() + ctx.channel().id() + "：" + request);
		JSONObject o = new JSONObject();
		try {
			o=JSONObject.fromObject(request);
		} catch (Exception e) {
			o.put("error", "数据格式错误");
		}
		o.put("onlineNumber", Global.getOnlineCount(ctx.attr(AttributeKey.valueOf("id")).get().toString()));
		TextWebSocketFrame tws = new TextWebSocketFrame(o.toString());
		o=null;
		// 群发
		//Global.group.writeAndFlush(tws);
		sendMessage(ctx.attr(AttributeKey.valueOf("id")).get().toString(),tws);
		// 返回【谁发的发给谁】
		// ctx.channel().writeAndFlush(tws);
	}

	private void handlerWebSocketFrame2(ChannelHandlerContext ctx, WebSocketFrame frame) {
		// 判断是否关闭链路的指令
		if (frame instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
			return;
		}
		// 判断是否ping消息
		if (frame instanceof PingWebSocketFrame) {
			ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
			return;
		}
		// 本例程仅支持文本消息，不支持二进制消息
		if (!(frame instanceof TextWebSocketFrame)) {
			System.out.println("本例程仅支持文本消息，不支持二进制消息");
			throw new UnsupportedOperationException(
					String.format("%s frame types not supported", frame.getClass().getName()));
		}
		// 返回应答消息
		String request = ((TextWebSocketFrame) frame).text();
		//System.out.println("服务端2收到：" + request);
		JSONObject o = new JSONObject();
		try {
			o=JSONObject.fromObject(request);
		} catch (Exception e) {
			o.put("error", "数据格式错误");
		}
		o.put("onlineNumber", Global.getOnlineCount(ctx.attr(AttributeKey.valueOf("id")).get().toString()));
		TextWebSocketFrame tws = new TextWebSocketFrame(o.toString());
		o=null;
		// 群发
		//Global.group.writeAndFlush(tws);
		sendMessage(ctx.attr(AttributeKey.valueOf("id")).get().toString(),tws);
		// 返回【谁发的发给谁】
		// ctx.channel().writeAndFlush(tws);
	}
	private void sendMessage(String id,TextWebSocketFrame tws){
		ChannelGroup channelGroup = Global.ChannelGroups.get(id);
		if(channelGroup!=null){
			channelGroup.writeAndFlush(tws);
		}
	}
	private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
		// 如果HTTP解码失败，返回HHTP异常
		if (!req.getDecoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) {
			sendHttpResponse(ctx, req,
					new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
			return;
		}
		// 获取url后置参数
		HttpMethod method = req.getMethod();
		String uri = req.getUri();
		//QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri);
		//Map<String, List<String>> parameters = queryStringDecoder.parameters();
		//System.out.println(parameters.get("request").get(0));
		if (method == HttpMethod.GET && uri.contains("/webssss")) {
			// ....处理
			ctx.attr(AttributeKey.valueOf("type")).set("anzhuo");
		} else if (method == HttpMethod.GET && uri.contains("/websocket")) {
			// ...处理
			ctx.attr(AttributeKey.valueOf("type")).set("live");
		}
		String id=null;
		try {
			id = uri.substring(uri.lastIndexOf("/")+1);
			ctx.attr(AttributeKey.valueOf("id")).set(id);
			Global.addChannelGroup(id, ctx.channel());
			
			
			
		} catch (Exception e) {
			return;
		}
		
		//Global.add(id, ctx.channel().id());
		
		// 构造握手响应返回，本机测试
		WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://" + req.headers().get(HttpHeaders.Names.HOST) + uri, null, false);
		handshaker = wsFactory.newHandshaker(req);
		if (handshaker == null) {
			WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
		} else {
			handshaker.handshake(ctx.channel(), req);
		}
		
		if(id!=null){
			JSONObject o = new JSONObject();
			o.put("onlineNumber", Global.getOnlineCount(id.toString()));
			TextWebSocketFrame tws = new TextWebSocketFrame(o.toString());
			o=null;
			sendMessage(id.toString(),tws);
			tws=null;
		}
		id=null;
	}

	private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
		// 返回应答给客户端
		if (res.getStatus().code() != 200) {
			ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
			res.content().writeBytes(buf);
			buf.release();
		}
		// 如果是非Keep-Alive，关闭连接
		ChannelFuture f = ctx.channel().writeAndFlush(res);
		if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != 200) {
			f.addListener(ChannelFutureListener.CLOSE);
		}
	}

	/**
	 * exception 异常 Caught 抓住 抓住异常，当发生异常的时候，可以做一些相应的处理，比如打印日志、关闭链接
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}
}
